namespace GYLite
{
    export class Vector4
    {
        public elements:Float32Array;
        constructor(x:number=0, y:number=0, z:number=0, w:number=0) {
            this.elements = new Float32Array(4);            
            var v:Float32Array = this.elements;
            v[0] = x;
            v[1] = y;
            v[2] = z;
            v[3] = w;
        }

        /**
         *从Array数组拷贝值。
         *@param array 数组。
         *@param offset 数组偏移。
         */
        public fromArray(array:number[], offset:number=0):void {            
            this.elements[0] = array[offset + 0];
            this.elements[1] = array[offset + 1];
            this.elements[2] = array[offset + 2];
            this.elements[3] = array[offset + 3];
        }

        /**
         *克隆。
         *@param destObject 克隆源。
         */
        public cloneTo(destObject:Vector4):void {
            var destVector4:Vector4 = destObject;
            var destE:Float32Array = destVector4.elements;
            var s:Float32Array = this.elements;
            destE[0] = s[0];
            destE[1] = s[1];
            destE[2] = s[2];
            destE[3] = s[3];
        }

        /**
         *克隆。
         *@return 克隆副本。
         */
        public clone():Vector4 {
            let cls:any;
            cls = this.constructor;
            var destVector4:Vector4 = new cls;
            this.cloneTo(destVector4);
            return destVector4;
        }

        /**
         *求四维向量的长度。
         *@return 长度。
         */
        public length():number {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        }

        /**
         *求四维向量长度的平方。
         *@return 长度的平方。
         */
        public lengthSquared():number {
            return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
        }

        /**
         *设置X轴坐标。
         *@param value X轴坐标。
         */
        /**
         *获取X轴坐标。
         *@return X轴坐标。
         */
        public get x():number{
            return this.elements[0];
        }
        public set x(value) {
            this.elements[0] = value;
        }

        /**
         *设置Y轴坐标。
         *@param value Y轴坐标。
         */
        /**
         *获取Y轴坐标。
         *@return Y轴坐标。
         */
        public get y():number {
            return this.elements[1];
        }
        public set y(value:number) {
            this.elements[1] = value;
        }

        /**
         *设置Z轴坐标。
         *@param value Z轴坐标。
         */
        /**
         *获取Z轴坐标。
         *@return Z轴坐标。
         */
        public get z():number {
            return this.elements[2];
        }
        public set z(value:number) {
            this.elements[2] = value;
        }

        /**
         *设置W轴坐标。
         *@param value W轴坐标。
         */
        /**
         *获取W轴坐标。
         *@return W轴坐标。
         */
        public get w():number {
            return this.elements[3];
        }
        public set w(value:number) {
            this.elements[3] = value;
        }

        public reset(x:number, y:number, z:number, w:number):void {
            this.elements[0] = x;
            this.elements[1] = y;
            this.elements[2] = z;
            this.elements[3] = w;
        }

        public static lerp(a:Vector4, b:Vector4, t:number, out:Vector4):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements;
            var ax:number = f[0], ay:number = f[1], az:number = f[2], aw:number = f[3];
            e[0] = ax + t * (g[0] - ax);
            e[1] = ay + t * (g[1] - ay);
            e[2] = az + t * (g[2] - az);
            e[3] = aw + t * (g[3] - aw);
        }

        public static transformByM4x4(vector4:Vector4, m4x4:Matrix4x4, out:Vector4):void {
            var ve:Float32Array = vector4.elements;
            var vx:number = ve[0];
            var vy:number = ve[1];
            var vz:number = ve[2];
            var vw:number = ve[3];
            var me:Float32Array = m4x4.elements;
            var oe:Float32Array = out.elements;
            oe[0] = vx * me[0] + vy * me[4] + vz * me[8] + vw * me[12];
            oe[1] = vx * me[1] + vy * me[5] + vz * me[9] + vw * me[13];
            oe[2] = vx * me[2] + vy * me[6] + vz * me[10] + vw * me[14];
            oe[3] = vx * me[3] + vy * me[7] + vz * me[11] + vw * me[15];
        }

        public static equals(a:Vector4, b:Vector4):boolean {
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            return MathUtils3D.nearEqual(Math.abs(ae[0]), Math.abs(be[0])) && MathUtils3D.nearEqual(Math.abs(ae[1]), Math.abs(be[1])) && MathUtils3D.nearEqual(Math.abs(ae[2]), Math.abs(be[2])) && MathUtils3D.nearEqual(Math.abs(ae[3]), Math.abs(be[3]));
        }

        public static normalize(s:Vector4, out:Vector4):void {
            var se:Float32Array = s.elements;
            var oe:Float32Array = out.elements;
            var len:number = /*if err,please use iflash.method.xmlLength()*/s.length();
            if (len > 0) {
                oe[0] = se[0] * len;
                oe[1] = se[1] * len;
                oe[2] = se[2] * len;
                oe[3] = se[3] * len;
            }
        }

        public static add(a:Vector4, b:Vector4, out:Vector4):void {
            var oe:Float32Array = out.elements;
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            oe[0] = ae[0] + be[0];
            oe[1] = ae[1] + be[1];
            oe[2] = ae[2] + be[2];
            oe[3] = ae[3] + be[3];
        }

        public static subtract(a:Vector4, b:Vector4, out:Vector4):void {
            var oe:Float32Array = out.elements;
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            oe[0] = ae[0] - be[0];
            oe[1] = ae[1] - be[1];
            oe[2] = ae[2] - be[2];
            oe[3] = ae[3] - be[3];
        }

        public static multiply(a:Vector4, b:Vector4, out:Vector4):void {
            var oe:Float32Array = out.elements;
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            oe[0] = ae[0] * be[0];
            oe[1] = ae[1] * be[1];
            oe[2] = ae[2] * be[2];
            oe[3] = ae[3] * be[3];
        }

        public static scale(a:Vector4, b:number, out:Vector4):void {
            var oe:Float32Array = out.elements;
            var ae:Float32Array = a.elements;
            oe[0] = ae[0] * b;
            oe[1] = ae[1] * b;
            oe[2] = ae[2] * b;
            oe[3] = ae[3] * b;
        }

        public static Clamp(value:Vector4, min:Vector4, max:Vector4, out:Vector4):void {
            var valuee:Float32Array = value.elements;
            var x:number = valuee[0];
            var y:number = valuee[1];
            var z:number = valuee[2];
            var w:number = valuee[3];
            var mine:Float32Array = min.elements;
            var mineX:number = mine[0];
            var mineY:number = mine[1];
            var mineZ:number = mine[2];
            var mineW:number = mine[3];
            var maxe:Float32Array = max.elements;
            var maxeX:number = maxe[0];
            var maxeY:number = maxe[1];
            var maxeZ:number = maxe[2];
            var maxeW:number = maxe[3];
            var oute:Float32Array = out.elements;
            x = (x > maxeX) ? maxeX : x;
            x = (x < mineX) ? mineX : x;
            y = (y > maxeY) ? maxeY : y;
            y = (y < mineY) ? mineY : y;
            z = (z > maxeZ) ? maxeZ : z;
            z = (z < mineZ) ? mineZ : z;
            w = (w > maxeW) ? maxeW : w;
            w = (w < mineW) ? mineW : w;
            oute[0] = x;
            oute[1] = y;
            oute[2] = z;
            oute[3] = w;
        }

        public static distanceSquared(value1:Vector4, value2:Vector4):number {
            var value1e:Float32Array = value1.elements;
            var value2e:Float32Array = value2.elements;
            var x:number = value1e[0] - value2e[0];
            var y:number = value1e[1] - value2e[1];
            var z:number = value1e[2] - value2e[2];
            var w:number = value1e[3] - value2e[3];
            return (x * x) + (y * y) + (z * z) + (w * w);
        }

        public static distance(value1:Vector4, value2:Vector4):number {
            var value1e:Float32Array = value1.elements;
            var value2e:Float32Array = value2.elements;
            var x:number = value1e[0] - value2e[0];
            var y:number = value1e[1] - value2e[1];
            var z:number = value1e[2] - value2e[2];
            var w:number = value1e[3] - value2e[3];
            return Math.sqrt((x * x) + (y * y) + (z * z) + (w * w));
        }

        public static dot(a:Vector4, b:Vector4):number {
            var ae:Float32Array = a.elements;
            var be:Float32Array = b.elements;
            var r:number = (ae[0] * be[0]) + (ae[1] * be[1]) + (ae[2] * be[2]) + (ae[3] * be[3]);
            return r;
        }

        public static min(a:Vector4, b:Vector4, out:Vector4):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = Math.min(f[0], g[0]);
            e[1] = Math.min(f[1], g[1]);
            e[2] = Math.min(f[2], g[2]);
            e[3] = Math.min(f[3], g[3]);
        }

        public static max(a:Vector4, b:Vector4, out:Vector4):void {
            var e:Float32Array = out.elements;
            var f:Float32Array = a.elements;
            var g:Float32Array = b.elements
            e[0] = Math.max(f[0], g[0]);
            e[1] = Math.max(f[1], g[1]);
            e[2] = Math.max(f[2], g[2]);
            e[3] = Math.max(f[3], g[3]);
        }

        public static ZERO:Vector4 = new Vector4(1,1,1,1);
        public static ONE:Vector4 = new Vector4(1,0,0,0);
        public static UnitX:Vector4 = new Vector4(0,1,0,0);
        public static UnitY:Vector4 = new Vector4(0,0,1,0);
        public static UnitZ:Vector4 = new Vector4(0,0,1,0);
        public static UnitW:Vector4 = new Vector4(0,0,0,1);
    }
}